package pl.edu.pw.MidEmu.Logic;

import java.io.File;
import java.io.IOException;

import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextPane;

import pl.edu.pw.MidEmu.Common.Models.ClassSettingsModel;
import pl.edu.pw.MidEmu.Logic.ProcessOutputHandler.OutputType;
import pl.edu.pw.MidEmu.UI.Controllers.RunningApplicationsListController;

/**
 * Service class responsible for handling processes
 * Launches and terminates processes
 * 
 * @author Mateusz Starodab
 *
 */
public class ProcessHandler extends Thread
{
	private String _appParameters;

	private String _classPath;

	private String _classToRunName;

	private String _JVMparameters;

	private String _packageName;

	private File _workingDirectory;

	private String componentName;
	private String execParams;

	private String execPath;

	private boolean isActive;

	private Boolean isJava;

	private ClassSettingsModel model;

	private JPanel panel;

	private int panelIndex;

	private Process process;

	private ProcessOutputHandler processErrorHandler;
	private ProcessOutputHandler processOutputHandler;

	private RunningApplicationsListController subscriber;

	/**
	 * Constructor for java application handlers
	 * @param componentName Hosting component name
	 * @param model Application class settings model
	 */
	public ProcessHandler(String componentName, ClassSettingsModel model)
	{
		this.model = model;
		isJava = true;
		_workingDirectory = new File(model.get_workDir());
		_packageName = model.get_package();
		_classToRunName = model.get_className();
		_JVMparameters = model.get_JVMparameters();
		_appParameters = model.get_parameters();
		_classPath = model.get_classPath();
		this.componentName = componentName;
		if (_classPath != null && !_classPath.equals(""))
			_classPath = "-classpath " + _classPath;
		else
			_classPath = "";
	}

	/**
	 * Constructor for non Java applications
	 * @param execPath Executable path
	 * @param params Parameters
	 * @param componentName Hosting component name
	 */
	public ProcessHandler(String execPath, String params, String componentName)
	{
		isJava = false;
		this.execPath = execPath;
		this.execParams = params;
		this.componentName = componentName;
	}

	/**
	 * Constructor for non Java applications with work directory
	 * @param execPath Executable path
	 * @param params Parameters
	 * @param componentName Hosting component name
	 * @param workDir Work directory
	 */
	public ProcessHandler(String execPath, String params, String componentName, String workDir)
	{
		isJava = false;
		this.execPath = execPath;
		this.execParams = params;
		this.componentName = componentName;
		_workingDirectory = new File(workDir);
	}

	/**
	 * Constructor for Java applications without model
	 * @param className class name to launch
	 * @param JVMparameters JVM parameters
	 * @param appParameters Application parameters
	 * @param workDir Working directory
	 * @param libPath Libraries path
	 * @param componentName Hosting component name
	 */
	public ProcessHandler(String className, String JVMparameters, String appParameters, String workDir, String libPath, String componentName)
	{
		isJava = true;
		_workingDirectory = new File(workDir);
		_classToRunName = className;
		_appParameters = appParameters;
		_JVMparameters = JVMparameters;
		_classPath = libPath;
		this.componentName = componentName;
		if (_classPath != null && !_classPath.equals(""))
			_classPath = "-classpath " + _classPath;
		else
			_classPath = "";
	}

	/**
	 * Gets application name
	 * @return application name
	 */
	public String getAppName()
	{
		return _classToRunName;
	}

	/**
	 * Gets class settings model
	 * @return Class settings model
	 */
	public ClassSettingsModel getModel()
	{
		return model;
	}

	/**
	 * Gets process identity
	 * @return String representation of process
	 */
	public String getProcessIdentity()
	{
		if (isJava)
			return componentName + " - " + _classToRunName;
		else
			return componentName;
	}

	/**
	 * Checks if process is active
	 * @return True - if process is active
	 */
	public boolean isActive()
	{
		return isActive;
	}

	@Override
	/**
	 * Loop method responsible for launching and supporting running process
	 */
	public void run()
	{
		try
		{
			if (isJava)
				if (_packageName != null && _packageName != "")
					process = Runtime.getRuntime().exec("java " + _classPath + " " + _JVMparameters + " " + _packageName + "." + _classToRunName + " " + _appParameters, null, _workingDirectory);
				else
					process = Runtime.getRuntime().exec("java " + _classPath + " " + _JVMparameters + " " + _classToRunName + " " + _appParameters, null, _workingDirectory);
			else
			{
				System.out.println(execPath + " " + execParams);
				if (_workingDirectory != null)
				{
					ProcessBuilder pb = new ProcessBuilder(execPath + " " + execParams);
					pb.directory(_workingDirectory);
					process = pb.start();
				}
				else
					process = Runtime.getRuntime().exec(execPath + " " + execParams);
			}
			isActive = true;

			processOutputHandler = new ProcessOutputHandler(process.getInputStream());
			processOutputHandler.setIdentity(getProcessIdentity());
			processOutputHandler.setOutputType(OutputType.Output);
			processOutputHandler.start();

			processErrorHandler = new ProcessOutputHandler(process.getErrorStream());
			processErrorHandler.setIdentity(getProcessIdentity());
			processErrorHandler.setOutputType(OutputType.Error);
			processErrorHandler.start();

			process.waitFor();

			isActive = false;

			if (panel != null)
			{
				((JTabbedPane) panel.getParent()).setTitleAt(panelIndex, getAppName() + " - Inactive");
			}
			if (subscriber != null)
			{
				subscriber.rebindModels();
			}

			processOutputHandler.terminate();
			processErrorHandler.terminate();
		}
		catch (IOException ex)
		{
			System.out.println("Can't start process. Can't get runtime.");
			ex.printStackTrace();
		}
		catch (InterruptedException e)
		{
			System.out.println("Interrupted");
			e.printStackTrace();
		}
	}

	/**
	 * Subscribes for process activity changes
	 * @param panel Panel to display data on
	 * @param index Index of panel
	 */
	public void subscribeForActivityChange(JPanel panel, int index)
	{
		this.panel = panel;
		this.panelIndex = index;
	}

	/**
	 * Subscribe for process activity change for RunningApplicationsListController
	 * @param runningApplicationsListController
	 */
	public void subscribeForActivityChange(RunningApplicationsListController runningApplicationsListController)
	{
		subscriber = runningApplicationsListController;
	}

	/**
	 * Subscribes for process error output
	 * @param botomText Panel to display error information on
	 */
	public void subscribeForErrorData(JTextPane botomText)
	{
		processErrorHandler.subscribeForData(botomText);
	}

	/**
	 * Subscribes for process output
	 * @param botomText Panel to display information on
	 */
	public void subscribeForOuputData(JTextPane topText)
	{
		processOutputHandler.subscribeForData(topText);
	}

	/**
	 * Terminates process
	 */
	public void terminate()
	{
		if (process != null)
		{
			System.out.println("Attempting to terminate process handler");
			processOutputHandler.markAsClosing();
			processErrorHandler.markAsClosing();
			process.destroy();
			System.out.println("Process - " + getProcessIdentity() + " - Terminated");
		}
	}
}
